using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

namespace Bludz
{
    class GameServer : Singleton<GameServer>
    {
        public GraphicsDeviceManager graphics;
        public ContentManager content;
        public SpriteBatch spriteBatch;

        public int screenw, screenh;

        private Dictionary<String, Entity> entityDict;

        private int numKeys;
        private bool[] keyState;

        private ButtonState leftMouse, rightMouse, middleMouse;
        private int mousex, mousey, mouseWheel;

        public GameServer()
        {
        }

        public void init(GraphicsDeviceManager _graphics, ContentManager _content)
        {
            graphics = _graphics;
            content = _content;

            entityDict = new Dictionary<String,Entity>();

            numKeys = Enum.GetValues(typeof(Keys)).Length;
            keyState = new bool[numKeys];
            for (int i = 0; i < numKeys; ++i)
                keyState[i] = false;

            leftMouse = new ButtonState();
            rightMouse = new ButtonState();
            middleMouse = new ButtonState();

            mousex = mousey = mouseWheel = 0;

            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);

            screenw = graphics.GraphicsDevice.Viewport.Width;
            screenh = graphics.GraphicsDevice.Viewport.Height;

            addEntity("BludzGame", new BludzGame());
        }

        public bool update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
                Keyboard.GetState().IsKeyDown(Keys.Escape))
                return false;

            updateKeyboardInput();
            updateMouseInput();

            EventServer.get().pull("update", gameTime);

            return true;
        }

        public void render(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            EventServer.get().pull("render", gameTime);

            spriteBatch.Begin();
            EventServer.get().pull("render2d", gameTime);
            spriteBatch.End();
        }

        private void updateKeyboardInput()
        {
            bool lastVal, val;

            bool shift = Keyboard.GetState().IsKeyDown(Keys.LeftShift) ||
                Keyboard.GetState().IsKeyDown(Keys.RightShift),
                ctrl = Keyboard.GetState().IsKeyDown(Keys.LeftControl) ||
                Keyboard.GetState().IsKeyDown(Keys.RightControl),
                alt = Keyboard.GetState().IsKeyDown(Keys.LeftAlt) ||
                Keyboard.GetState().IsKeyDown(Keys.RightAlt);

            for (int i = 0; i < numKeys; ++i)
            {
                lastVal = keyState[i];
                Keys key = (Keys)Enum.GetValues(typeof(Keys)).GetValue(i);
                val = Keyboard.GetState().IsKeyDown(key);
                if (val && !lastVal)
                    EventServer.get().pull("keyDown", key, shift, ctrl, alt);
                if (!val && lastVal)
                    EventServer.get().pull("keyUp", key, shift, ctrl, alt);
                keyState[i] = val;
            }
        }

        private void updateMouseInput()
        {
            float relx = (float)mousex / (float)screenw;
            float rely = (float)mousey / (float)screenh;
            int newMousex = Mouse.GetState().X, newMousey = Mouse.GetState().Y;
            if (newMousex != mousex || newMousey != mousey)
            {
                mousex = newMousex;
                mousey = newMousey;
                relx = (float)mousex / (float)screenw;
                rely = (float)mousey / (float)screenh;
                EventServer.get().pull("mouseMotion", mousex, mousey, relx, rely);
            }

            ButtonState newLeftMouse = Mouse.GetState().LeftButton,
                newRightMouse = Mouse.GetState().RightButton,
                newMiddleMouse = Mouse.GetState().MiddleButton;

            if (leftMouse == ButtonState.Pressed && newLeftMouse == ButtonState.Released)
                EventServer.get().pull("mouseButtonUp", 0, mousex, mousey, relx, rely);
            if (leftMouse == ButtonState.Released && newLeftMouse == ButtonState.Pressed)
                EventServer.get().pull("mouseButtonDown", 0, mousex, mousey, relx, rely);
            leftMouse = newLeftMouse;

            if (rightMouse == ButtonState.Pressed && newRightMouse == ButtonState.Released)
                EventServer.get().pull("mouseButtonUp", 1, mousex, mousey, relx, rely);
            if (rightMouse == ButtonState.Released && newRightMouse == ButtonState.Pressed)
                EventServer.get().pull("mouseButtonDown", 1, mousex, mousey, relx, rely);
            rightMouse = newRightMouse;

            if (middleMouse == ButtonState.Pressed && newMiddleMouse == ButtonState.Released)
                EventServer.get().pull("mouseButtonUp", 2, mousex, mousey, relx, rely);
            if (middleMouse == ButtonState.Released && newMiddleMouse == ButtonState.Pressed)
                EventServer.get().pull("mouseButtonDown", 2, mousex, mousey, relx, rely);
            middleMouse = newMiddleMouse;

            int newMouseWheel = Mouse.GetState().ScrollWheelValue;
            if (mouseWheel != newMouseWheel)
            {
                mouseWheel = newMouseWheel;
                EventServer.get().pull("mouseWheel", mouseWheel, mousex, mousey, relx, rely);
            }
        }

        /// <summary>
        /// Is there an entity with this name?
        /// </summary>
        /// <param name="name">the name</param>
        /// <returns>whether it's there</returns>
        public bool hasEntity(String name)
        {
            return name != null && entityDict.ContainsKey(name);
        }

        private String makeEntityName(String baseName)
        {
            if (!hasEntity(baseName))
                return baseName;
            else
            {
                int i = 2;
                String entityName;
                do
                {
                    entityName = baseName + "_" + i.ToString();
                } while (hasEntity(entityName));
                return entityName;
            }
        }

        public Entity addEntity(String name, Entity entity)
        {
            if (hasEntity(name))
                removeEntity(name);
            entity.name = name;
            entityDict[name] = entity;
            return entity;
        }

        ///// <summary>
        ///// Adds a new entity into the game
        ///// </summary>
        ///// <param name="baseName">The desired name for the entity</param>
        ///// <param name="type">The type of the entity</param>
        ///// <returns>The new entity</returns>
        //public Entity addEntity(String baseName, String type)
        //{
        //    String name = makeEntityName(baseName);

        //    String typeName = "Bludz." + type;
        //    Type entityType = Type.GetType(typeName);
        //    Entity entity;
        //    if (entityType != null && entityType.IsSubclassOf(typeof(Entity)))
        //        entity = (Entity)entityType.GetConstructor(
        //            new Type[] {typeof(String), typeof(String) }).Invoke(
        //            new Object[] {name,type});
        //    else
        //        entity = new Entity(name, type);

        //    entityDict[name] = entity;
        //    return entity;
        //}

        /// <summary>
        /// Removes an entity from the system
        /// </summary>
        /// <param name="name">The name of the enity</param>
        /// <returns>success</returns>
        public bool removeEntity(String name)
        {
            //EventServer.get().removeEntity(name); //fixme
            return entityDict.Remove(name);
        }

        /// <summary>
        /// Fetches an entity if it is there
        /// </summary>
        /// <param name="name">The name of the entity</param>
        /// <returns>The entity or null if it wasn't found</returns>
        public Entity getEntity(String name)
        {
            if (hasEntity(name))
                return entityDict[name];
            else
                return null;
        }
    }
}
